import {
  Injectable,
  UnauthorizedException,
  ConflictException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { UserService } from '../user/user.service';
import { CreateUserDto } from '../user/dto/create-user.dto';
import { LoginUserDto } from '../user/dto/login-user.dto';
import { User } from '../user/entities/user.entity';
import { ErrorCode } from '../../common/constants/error-code.enum';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import type { Cache } from 'cache-manager';
import { Inject } from '@nestjs/common';
import { CaptchaService } from 'src/common/services/captcha/captcha.service';

@Injectable()
export class AuthService {
  constructor(
    private readonly userService: UserService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
    @Inject(CaptchaService)
    private readonly captchaService: CaptchaService,
  ) {}

  /**
   * 用户注册
   * @param createUserDto 创建用户DTO
   * @returns 注册结果
   */
  async register(
    createUserDto: CreateUserDto,
    captchaId: string,
  ): Promise<{ user: User; token: string }> {
    const redisCaptcha = (await this.cacheManager.get(
      'captcha_' + captchaId,
    )) as string;
    if (
      !redisCaptcha ||
      !this.captchaService.validateCaptcha(createUserDto.captcha, redisCaptcha)
    ) {
      throw new ConflictException({
        code: ErrorCode.PARAMS_ERROR,
        message: '验证码错误',
      });
    }
    // 检查用户名是否已存在
    const existingUser = await this.userService.findOneByUsername(
      createUserDto.username,
    );
    if (existingUser) {
      throw new ConflictException({
        code: ErrorCode.USER_ALREADY_EXISTS,
        message: '用户名已存在',
      });
    }

    // 检查邮箱是否已存在
    if (createUserDto.email) {
      const existingEmail = await this.userService.findOneByEmail(
        createUserDto.email,
      );
      if (existingEmail) {
        throw new ConflictException({
          code: ErrorCode.PARAMS_ERROR,
          message: '邮箱已存在',
        });
      }
    }

    // 创建新用户
    const user = await this.userService.create(createUserDto);

    // 生成JWT令牌
    const token = this.generateToken(user);

    // 存储令牌到Redis
    await this.storeTokenInRedis(user.id, token);

    return { user, token };
  }

  /**
   * 用户登录
   * @param loginUserDto 登录用户DTO
   * @returns 登录结果
   */
  async login(
    loginUserDto: LoginUserDto,
  ): Promise<{ user: User; token: string }> {
    const { username, password } = loginUserDto;

    // 查找用户
    const user = await this.userService.findOneByUsername(username);
    if (!user) {
      throw new UnauthorizedException({
        code: ErrorCode.USER_NOT_FOUND,
        message: '用户名或密码错误',
      });
    }

    // 验证密码
    const isPasswordValid = await user.validatePassword(password);
    if (!isPasswordValid) {
      throw new UnauthorizedException({
        code: ErrorCode.PASSWORD_ERROR,
        message: '用户名或密码错误',
      });
    }

    // 检查用户是否被禁用
    if (!user.isActive) {
      throw new UnauthorizedException({
        code: ErrorCode.USER_NOT_FOUND,
        message: '用户已被禁用',
      });
    }

    // 生成JWT令牌
    const token = this.generateToken(user);

    // 存储令牌到Redis
    await this.storeTokenInRedis(user.id, token);

    return { user, token };
  }

  /**
   * 生成JWT令牌
   * @param user 用户信息
   * @returns JWT令牌
   */
  private generateToken(user: User): string {
    const payload = {
      sub: user.id,
      username: user.username,
      roles: user.roles,
    };
    return this.jwtService.sign(payload);
  }

  /**
   * 将令牌存储到Redis
   * @param userId 用户ID
   * @param token JWT令牌
   */
  private async storeTokenInRedis(
    userId: string,
    token: string,
  ): Promise<void> {
    // 获取JWT过期时间配置
    const expiresInConfig =
      this.configService.get<string>('jwt.expiresIn') || '1d';

    // 将时间字符串转换为秒数
    let expiresInSeconds: number;
    if (typeof expiresInConfig === 'number') {
      expiresInSeconds = expiresInConfig;
    } else if (expiresInConfig.endsWith('d')) {
      // 处理天数，例如'1d'
      const days = parseInt(expiresInConfig.slice(0, -1), 10);
      expiresInSeconds = days * 24 * 60 * 60;
    } else if (expiresInConfig.endsWith('h')) {
      // 处理小时，例如'12h'
      const hours = parseInt(expiresInConfig.slice(0, -1), 10);
      expiresInSeconds = hours * 60 * 60;
    } else if (expiresInConfig.endsWith('m')) {
      // 处理分钟，例如'30m'
      const minutes = parseInt(expiresInConfig.slice(0, -1), 10);
      expiresInSeconds = minutes * 60;
    } else if (expiresInConfig.endsWith('s')) {
      // 处理秒数，例如'60s'
      expiresInSeconds = parseInt(expiresInConfig.slice(0, -1), 10);
    } else {
      // 默认处理为秒数
      expiresInSeconds = parseInt(expiresInConfig, 10) || 3600;
    }

    const key = `auth:token:${userId}`;
    await this.cacheManager.set(key, token, expiresInSeconds * 1000);
  }

  /**
   * 验证用户
   * @param payload JWT载荷
   * @returns 用户信息
   */
  async validateUser(payload: any): Promise<User> {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
    const user = await this.userService.findOne(payload.sub);
    if (!user || !user.isActive) {
      throw new UnauthorizedException({
        code: ErrorCode.TOKEN_INVALID,
        message: '无效的令牌',
      });
    }

    // 验证令牌是否在Redis中
    const key = `auth:token:${user.id}`;
    const storedToken = await this.cacheManager.get(key);
    if (!storedToken) {
      throw new UnauthorizedException({
        code: ErrorCode.TOKEN_EXPIRED,
        message: '令牌已过期',
      });
    }

    return user;
  }

  /**
   * 注销用户
   * @param userId 用户ID
   */
  async logout(userId: string): Promise<void> {
    const key = `auth:token:${userId}`;
    await this.cacheManager.del(key);
  }
}
